#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/bug.h>
#include <linux/minmax.h>
#include <linux/errno.h>
#include <linux/math.h>

#include "../inc/tick.h"

static inline int set_next_event(unsigned long evt, struct clock_event_device *dev)
{
    return dev->ops->set_next_event(dev, evt);
}

static inline int set_next_ktime(ktime_t tim, struct clock_event_device *dev)
{
    return dev->ops->set_next_ktime(dev, tim);
}

static inline int set_state(struct clock_event_device *dev, int s)
{
    return dev->ops->set_state(dev, s);
}

static u64 cev_delta2ns(unsigned long latch, struct clock_event_device *evt,
                        bool ismax)
{
    u64 clc = (u64)latch << evt->shift;
    u64 rnd;

    if (WARN_ON(!evt->mult))
        evt->mult = 1;
    rnd = (u64)evt->mult - 1;

    /*
     * Upper bound sanity check. If the backwards conversion is
     * not equal latch, we know that the above shift overflowed.
     */
    if ((clc >> evt->shift) != (u64)latch)
        clc = ~0ULL;

    /*
     * Scaled math oddities:
     *
     * For mult <= (1 << shift) we can safely add mult - 1 to
     * prevent integer rounding loss. So the backwards conversion
     * from nsec to device ticks will be correct.
     *
     * For mult > (1 << shift), i.e. device frequency is > 1GHz we
     * need to be careful. Adding mult - 1 will result in a value
     * which when converted back to device ticks can be larger
     * than latch by up to (mult - 1) >> shift. For the min_delta
     * calculation we still want to apply this in order to stay
     * above the minimum device ticks limit. For the upper limit
     * we would end up with a latch value larger than the upper
     * limit of the device, so we omit the add to stay below the
     * device upper boundary.
     *
     * Also omit the add if it would overflow the u64 boundary.
     */
    if ((~0ULL - clc > rnd) &&
        (!ismax || evt->mult <= (1ULL << evt->shift)))
        clc += rnd;

    do_div(clc, evt->mult);

    /* Deltas less than 1usec are pointless noise */
    return clc > 1000 ? clc : 1000;
}

static inline void clockevents_calc_mult_shift(struct clock_event_device *ce, u32 freq, u32 maxsec)
{
    return clocks_calc_mult_shift(&ce->mult, &ce->shift, NSEC_PER_SEC, freq, maxsec);
}

static void clockevents_config(struct clock_event_device *dev, u32 freq)
{
    u64 sec;

    if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
        return;

    /*
     * Calculate the maximum number of seconds we can sleep. Limit
     * to 10 minutes for hardware which can program more than
     * 32bit ticks so we still get reasonable conversion values.
     */
    sec = dev->max_delta_ticks;
    do_div(sec, freq);
    if (!sec)
        sec = 1;
    else if (sec > 600 && dev->max_delta_ticks > UINT_MAX)
        sec = 600;

    clockevents_calc_mult_shift(dev, freq, sec);
    dev->min_delta_ns = cev_delta2ns(dev->min_delta_ticks, dev, false);
    dev->max_delta_ns = cev_delta2ns(dev->max_delta_ticks, dev, true);
}

static int __clockevents_switch_state(struct clock_event_device *dev,
                                      enum clock_event_state state)
{
    int ret = -ENOSYS;

    if (dev->features & CLOCK_EVT_FEAT_DUMMY)
        return 0;

    /* Transition with new state-specific callbacks */
    switch (state)
    {
    case CLOCK_EVT_STATE_ONESHOT:
    {
        if ((dev->features & CLOCK_EVT_FEAT_ONESHOT))
        {
            ret = set_state(dev, state);
        }
    }
    break;
    default:
        break;
    }

    return ret;
}

static int clockevents_program_min_delta(struct clock_event_device *dev)
{
    uint64_t clc;
    int64_t delta = 0;

    delta += dev->min_delta_ns;
    dev->next_event = ktime_add_ns(ktime_get(), delta);

    clc = ((uint64_t) delta * dev->mult) >> dev->shift;

    set_next_event(clc, dev);

    return 0;
}

/**********************************************************************/
/**
 * clockevents_config_and_register - Configure and register a clock event device
 * @dev:	device to register
 * @freq:	The clock frequency
 * @min_delta:	The minimum clock ticks to program in oneshot mode
 * @max_delta:	The maximum clock ticks to program in oneshot mode
 *
 * min/max_delta can be 0 for devices which do not support oneshot mode.
 */
void clockevents_config_and_register(struct clock_event_device *dev,
                                     unsigned int freq, unsigned long min_delta,
                                     unsigned long max_delta)
{
    dev->min_delta_ticks = min_delta;
    dev->max_delta_ticks = max_delta;
    clockevents_config(dev, freq);
    clockevents_register_device(dev);
}

void clockevents_register_device(struct clock_event_device *dev)
{
    tick_check_new_device(dev);
}

void clockevents_handle(struct clock_event_device *dev)
{
    dev->event_handler(dev);
}

/**
 * clockevents_switch_state - set the operating state of a clock event device
 * @dev:	device to modify
 * @state:	new state
 *
 * Must be called with interrupts disabled !
 */
void clockevents_switch_state(struct clock_event_device *dev, enum clock_event_state state)
{
    if (clockevent_get_state(dev) != state)
    {
        if (__clockevents_switch_state(dev, state))
            return;

        clockevent_set_state(dev, state);

        /*
         * A nsec2cyc multiplicator of 0 is invalid and we'd crash
         * on it, so fix it up and emit a warning:
         */
        if (clockevent_state_oneshot(dev))
        {
            if (WARN_ON(!dev->mult))
                dev->mult = 1;
        }
    }
}

/**
 * clockevents_program_event - Reprogram the clock event device.
 * @dev:	device to program
 * @expires:	absolute expiry time (monotonic clock)
 * @force:	program minimum delay if expires can not be set
 *
 * Returns 0 on success, -ETIME when the event is in the past.
 */
int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
                              bool force)
{
    unsigned long long clc;
    int64_t delta;
    int rc;

    if (WARN_ON_ONCE(expires < 0))
        return -ETIME;

    dev->next_event = expires;

    if (clockevent_state_shutdown(dev))
        return 0;

    /* We must be in ONESHOT state here */
    WARN_ONCE(!clockevent_state_oneshot(dev), "Current state: %d\n",
              clockevent_get_state(dev));

    /* Shortcut for clockevent devices that can deal with ktime. */
    if (dev->features & CLOCK_EVT_FEAT_KTIME)
        return set_next_ktime(expires, dev);

    delta = ktime_to_ns(ktime_sub(expires, ktime_get()));
    if (delta <= 0)
        return force ? clockevents_program_min_delta(dev) : -ETIME;

    delta = min(delta, (int64_t)dev->max_delta_ns);
    delta = max(delta, (int64_t)dev->min_delta_ns);

    clc = ((unsigned long long)delta * dev->mult) >> dev->shift;
    rc = set_next_event((unsigned long)clc, dev);

    return (rc && force) ? clockevents_program_min_delta(dev) : rc;
}
